home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume3 / modula_pp < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  36.4 KB

  1. From: Ken Yap  <talcott!seismo!rochester!ken>
  2. Subject: Modula-2 prettyprinter
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 35
  7. Submitted by: Ken Yap  <talcott!seismo!rochester!ken>
  8.  
  9.  
  10. This is the source for a Modula-2 prettyprinter, written in Modula-2.
  11. I believe everything needed, including a Makefile, is in the shar archive
  12. below.
  13.  
  14.     Cheers, Ken
  15. --
  16. #!/bin/sh
  17. # This is a shell archive, meaning:
  18. # 1. Remove everything above the #!/bin/sh line.
  19. # 2. Save the resulting text in a file.
  20. # 3. Execute the file with /bin/sh (not csh) to create the files:
  21. #    README
  22. #    Makefile
  23. #    m2p.mod
  24. #    InOut.def
  25. #    InOut.c
  26. # This archive created: Sat Nov  2 02:03:56 1985
  27. # By:    Ken Yap (U of Rochester, CS Dept)
  28. export PATH; PATH=/bin:$PATH
  29. if test -f 'README'
  30. then
  31.     echo shar: over-writing existing file "'README'"
  32. fi
  33. cat << \SHAR_EOF > 'README'
  34. This is a Modula-2 prettyprinter. It takes a valid program from input
  35. and writes a formatted version to output. If it runs into syntax errors
  36. it may stop formatting and copy the rest of the file verbatim.  It
  37. isn't exactly the best example of modularity, but...
  38.  
  39. It uses the standard InOut module. An implementation of this module and
  40. Makefile for the DECWRL compiler under 4.2 BSD is included as an
  41. example.  You may need to make some minor changes for other Modula-2
  42. systems.
  43.  
  44. I wanted to put more stuff in but I got tired of having it around so I
  45. am pushing it out the door. I would be grateful if you would report any
  46. bugs or enhancements so that I can collect and redistribute the
  47. changes.  I have tried to make it as OS independent as possible.
  48. Please remember that Modula-2 runs on many systems. If you make changes
  49. that are specific to a machine/OS, please put the changes in specific
  50. modules or procedures.
  51.  
  52. That is all, I think. Have fun.
  53.  
  54.     Ken Yap
  55.     Dept. of Comp. Sci., U of Rochester
  56.     1st November 1985
  57.  
  58.     UUCP: ..!{seismo,decvax,allegra}!rochester!ken
  59.     ARPA: ken@rochester.arpa
  60. SHAR_EOF
  61. if test 1090 -ne "`wc -c 'README'`"
  62. then
  63.     echo shar: error transmitting "'README'" '(should have been 1090 characters)'
  64. fi
  65. if test -f 'Makefile'
  66. then
  67.     echo shar: over-writing existing file "'Makefile'"
  68. fi
  69. cat << \SHAR_EOF > 'Makefile'
  70. m2p:    m2p.o InOut.o
  71.     mod -g -o m2p m2p.o InOut.o
  72.  
  73. m2p.o:    m2p.mod
  74.     mod -s -g -c m2p.mod
  75.  
  76. InOut.o:    InOut.c
  77.     cc -O -c InOut.c
  78. SHAR_EOF
  79. if test 122 -ne "`wc -c 'Makefile'`"
  80. then
  81.     echo shar: error transmitting "'Makefile'" '(should have been 122 characters)'
  82. fi
  83. if test -f 'm2p.mod'
  84. then
  85.     echo shar: over-writing existing file "'m2p.mod'"
  86. fi
  87. cat << \SHAR_EOF > 'm2p.mod'
  88. MODULE Modula2PrettyPrinter;
  89.  
  90. FROM InOut IMPORT
  91.     Done, Read, Write, WriteLn, WriteString;
  92.  
  93. (*
  94. **      Modula-2 Prettyprinter, November 1985.
  95. **
  96. **      by Ken Yap, U of Rochester, CS Dept.
  97. **
  98. **      Permission to copy, modify, and distribute, but not for profit,
  99. **      is hereby granted, provided that this note is included.
  100. **
  101. **      adapted from a Pascal Program Formatter
  102. **      by J. E. Crider, Shell Oil Company,
  103. **      Houston, Texas 77025
  104. **
  105. **      This program formats Modula-2 programs according
  106. **      to structured formatting principles
  107. **
  108. **      A valid Modula-2 program is read from the input and
  109. **      a formatted program is written to the output.
  110. **      It is basically a recursive descent parser with actions
  111. **      intermixed with syntax scanning.
  112. **
  113. **      The actions of the program are as follows:
  114. **
  115. **      FORMATTING:  Each structured statement is formatted
  116. **      in the following pattern (with indentation "indent"):
  117. **
  118. **                XXXXXX header XXXXXXXX
  119. **                        XXXXXXXXXXXXXXXXXX
  120. **                        XXXXX body XXXXXX
  121. **                        XXXXXXXXXXXXXXXXXX
  122. **                END
  123. **
  124. **      where the header is one of:
  125. **
  126. **                IF <expression> THEN
  127. **                ELSIF <expression> THEN
  128. **                ELSE
  129. **                WHILE <expression> DO
  130. **                FOR <control variable> := <FOR list> DO
  131. **                WITH <RECORD variable> DO
  132. **                REPEAT
  133. **                LOOP
  134. **                CASE <expression> OF
  135. **                <CASE label list>:
  136. **
  137. **      and the last line begins with UNTIL or is END.
  138. **      Other program parts are formatted similarly.  The headers are:
  139. **
  140. **                <MODULE/PROCEDURE heading>;
  141. **                CONST
  142. **                TYPE
  143. **                VAR
  144. **                BEGIN
  145. **                (various FOR records AND RECORD variants)
  146. **
  147. **      COMMENTS:  Each comment that starts before or on a specified
  148. **      column on an input line (program constant "commthresh") is
  149. **      copied without shifting or reformatting.  Each comment that
  150. **      starts after "commthresh" is reformatted and left-justified
  151. **      following the aligned comment base column ("alcommbase").
  152. **
  153. **      SPACES AND BLANK LINES:  Spaces not at line breaks are copied from
  154. **      the input.  Blank lines are copied from the input if they appear
  155. **      between statements (or appropriate declaration units).  A blank
  156. **      line is inserted above each significant part of each program/
  157. **      procedure if one is not already there.
  158. **
  159. **      CONTINUATION:  Lines that are too long for an output line are
  160. **      continued with additional indentation ("contindent").
  161. *)
  162.  
  163. CONST
  164.     TAB = 11C;
  165.     NEWLINE = 12C;                  (* for Unix *)
  166.     FF = 14C;
  167.     maxrwlen = 15;                  (* size of reserved word strings *)
  168.     ordminchar = 0;                 (* ord of lowest char in char set *)
  169.     ordmaxchar = 127;               (* ord of highest char in char set *)
  170. (* The following parameters may be adjusted for the installation: *)
  171.     maxinlen = 255;                 (* maximum width of input line + 1 *)
  172.     maxoutlen = 128;                (* maximum width of output line *)
  173.     tabinterval = 8;                (* interval between tab columns *)
  174.     initmargin = 0;                 (* initial value of output margin *)
  175.     commthresh = tabinterval;       (* column threshhold in input for comments to be aligned *)
  176.     alcommbase = 40;                (* aligned comments in output start after this column *)
  177.     indent = tabinterval;           (* RECOMMENDED indentation increment *)
  178.     contindent = tabinterval;       (* continuation indentation, >indent *)
  179.     commindent = tabinterval;       (* comment continuation indentation *)
  180.  
  181. TYPE
  182.     natural = [-1..1000000];        (* kludge because compiler doesn't *)
  183.     inrange = [-1..maxinlen];       (* recognize qualified subranges *)
  184.     outrange = [-1..maxoutlen];
  185.  
  186.     errortype = (longline, noendcomm, notquote, longword, notdo, notof, notend, notthen, notbegin, notuntil, notident,
  187.     notsemicolon, notcolon, notperiod, notparen, noeof);
  188.  
  189.     chartype = (illegal, special, chapostrophe, chleftparen, chrightparen, chperiod, digit, chcolon, chsemicolon,
  190.     chlessthan, chgreaterthan, letter, chleftbrace, chbar);
  191.  
  192.     chartypeset = SET OF chartype;  (* for reserved word recognition *)
  193.  
  194.     resword = (                     (* reserved words ordered by length *)
  195.     rwif, rwdo, rwof, rwto, rwin, rwor,
  196.                     (* length: 2 *)
  197.     rwend, rwfor, rwvar, rwdiv, rwmod, rwset, rwand, rwnot, rwnil,
  198.                     (* length: 3 *)
  199.     rwthen, rwelse, rwwith, rwcase, rwtype, rwloop, rwfrom,
  200.                     (* length: 4 *)
  201.     rwbegin, rwelsif, rwuntil, rwwhile, rwarray, rwconst,
  202.                     (* length: 5 *)
  203.     rwrepeat, rwrecord, rwmodule, rwimport, rwexport,
  204.                     (* length: 6 *)
  205.     rwpointer,                      (* length: 7 *)
  206.     rwprocedure, rwqualified,       (* length: 9 *)
  207.     rwdefinition,                   (* length: 10 *)
  208.     rwimplementation,               (* length: 14 *)
  209.     rwx);                           (* length: 15 for table sentinel *)
  210.     rwstring =  ARRAY [1..maxrwlen] OF CHAR;
  211.  
  212.     firstclass = (                  (* class of word if on new line *)
  213.     newclause,                      (* start of new clause *)
  214.     continue,                       (* continuation of clause *)
  215.     alcomm,                         (* start of aligned comment *)
  216.     contalcomm,                     (* continuation of aligned comment *)
  217.     uncomm,                         (* start of unaligned comment *)
  218.     contuncomm);                    (* continuation of unaligned comment *)
  219.  
  220.     wordtype = RECORD               (* data record for word *)
  221.         whenfirst : firstclass; (* class of word if on new line *)
  222.         puncfollows : BOOLEAN;  (* to reduce dangling punctuation *)
  223.         blanklncount : natural; (* number of preceding blank lines *)
  224.         spaces : INTEGER;       (* number of spaces preceding word *)
  225.         base : [-1..maxinlen];  (* inline.buf[base] precedes word *)
  226.         size : inrange;
  227.     END;                            (* length of word in inline.buf *)
  228.  
  229.     symboltype = (                  (* symbols for syntax analysis *)
  230.     symodule, sydefinition, syimplementation, syfrom, syimport, syexport, syqual, syproc, declarator, sybegin, syend, syif,
  231.     sythen, syelsif, syelse, syloop, sycase, syof, syuntil, syrepeat, forwhilewith, sydo, syrecord, ident, intconst,
  232.     semicolon, leftparen, rightparen, period, colon, bar, othersym, otherword, comment, syeof);
  233.     symbolset = SET OF symboltype;
  234.  
  235. VAR
  236.     inline : RECORD                 (* input line data *)
  237.         endoffile : BOOLEAN;    (* end of file on input? *)
  238.         ch : CHAR;              (* current char, buf[index] *)
  239.         index : inrange;        (* subscript of current char *)
  240.         len : natural;          (* length of input line in buf *)
  241.         buf : ARRAY [1..maxinlen] OF CHAR;
  242.     END;
  243.     outline : RECORD                (* output line data *)
  244.         blanklns : natural;     (* number of preceding blank lines *)
  245.         len : outrange;         (* number of chars in buf *)
  246.         buf : ARRAY [1..maxoutlen] OF CHAR;
  247.     END;
  248.     curword : wordtype;             (* current word *)
  249.     margin : outrange;              (* left margin *)
  250.     lnpending : BOOLEAN;            (* new line before next symbol? *)
  251.     inheader : BOOLEAN;             (* are we scanning a proc header? *)
  252.     symbol : symboltype;            (* current symbol *)
  253.  
  254.   (* Structured Constants *)
  255.     headersyms : symbolset;         (* headers for program parts *)
  256.     strucsyms : symbolset;          (* symbols that begin structured statements *)
  257.     stmtbeginsyms : symbolset;      (* symbols that begin statements *)
  258.     stmtendsyms : symbolset;        (* symbols that follow statements *)
  259.     stopsyms : symbolset;           (* symbols that stop expression scan *)
  260.     recendsyms : symbolset;         (* symbols that stop record scan *)
  261.     datawords : symbolset;          (* to reduce dangling punctuation *)
  262.     firstrw : ARRAY [1..maxrwlen] OF resword;
  263.     rwword : ARRAY [rwif..rwimplementation] OF rwstring;
  264.     rwsy : ARRAY [rwif..rwimplementation] OF symboltype;
  265.     charclass : ARRAY CHAR OF chartype;
  266.     symbolclass : ARRAY chartype OF symboltype;
  267.  
  268. PROCEDURE StructConsts;
  269. (* establish values of structured constants *)
  270. VAR
  271.     i : [ordminchar..ordmaxchar];   (* loop index *)
  272.     ch : CHAR;                      (* loop index *)
  273.  
  274. PROCEDURE BuildResWord(rw : resword; symword : rwstring; symbol : symboltype);
  275. BEGIN
  276.     rwword[rw] := symword;          (* reserved word string *)
  277.     rwsy[rw] := symbol;             (* map to symbol *)
  278. END BuildResWord;
  279.  
  280. BEGIN                                   (* StructConsts *)
  281. (* symbol sets for syntax analysis *)
  282.     headersyms := symbolset{symodule, syproc, declarator, sybegin, syend, syeof};
  283.     strucsyms := symbolset{sycase, syrepeat, syif, forwhilewith, syloop};
  284.     stmtbeginsyms := strucsyms + symbolset{ident};
  285.     stmtendsyms := symbolset{semicolon, bar, syend, syuntil, syelsif, syelse, syeof};
  286.     stopsyms := headersyms + strucsyms + stmtendsyms;
  287.     recendsyms := symbolset{rightparen, syend, syeof};
  288.     datawords := symbolset{otherword, intconst, ident, syend};
  289.  
  290. (* constants for recognizing reserved words *)
  291.     firstrw[1] := rwif;             (* length: 1 *)
  292.     firstrw[2] := rwif;             (* length: 2 *)
  293.     BuildResWord(rwif, 'IF             ', syif);
  294.     BuildResWord(rwdo, 'DO             ', sydo);
  295.     BuildResWord(rwof, 'OF             ', syof);
  296.     BuildResWord(rwto, 'TO             ', othersym);
  297.     BuildResWord(rwin, 'IN             ', othersym);
  298.     BuildResWord(rwor, 'OR             ', othersym);
  299.     firstrw[3] := rwend;            (* length: 3 *)
  300.     BuildResWord(rwend, 'END            ', syend);
  301.     BuildResWord(rwfor, 'FOR            ', forwhilewith);
  302.     BuildResWord(rwvar, 'VAR            ', declarator);
  303.     BuildResWord(rwdiv, 'DIV            ', othersym);
  304.     BuildResWord(rwmod, 'MOD            ', othersym);
  305.     BuildResWord(rwset, 'SET            ', othersym);
  306.     BuildResWord(rwand, 'AND            ', othersym);
  307.     BuildResWord(rwnot, 'NOT            ', othersym);
  308.     BuildResWord(rwnil, 'NIL            ', otherword);
  309.     firstrw[4] := rwthen;           (* length: 4 *)
  310.     BuildResWord(rwthen, 'THEN           ', sythen);
  311.     BuildResWord(rwelse, 'ELSE           ', syelse);
  312.     BuildResWord(rwwith, 'WITH           ', forwhilewith);
  313.     BuildResWord(rwloop, 'LOOP           ', syloop);
  314.     BuildResWord(rwfrom, 'FROM           ', syfrom);
  315.     BuildResWord(rwcase, 'CASE           ', sycase);
  316.     BuildResWord(rwtype, 'TYPE           ', declarator);
  317.     firstrw[5] := rwbegin;          (* length: 5 *)
  318.     BuildResWord(rwbegin, 'BEGIN          ', sybegin);
  319.     BuildResWord(rwelsif, 'ELSIF          ', syelsif);
  320.     BuildResWord(rwuntil, 'UNTIL          ', syuntil);
  321.     BuildResWord(rwwhile, 'WHILE          ', forwhilewith);
  322.     BuildResWord(rwarray, 'ARRAY          ', othersym);
  323.     BuildResWord(rwconst, 'CONST          ', declarator);
  324.     firstrw[6] := rwrepeat;         (* length: 6 *)
  325.     BuildResWord(rwrepeat, 'REPEAT         ', syrepeat);
  326.     BuildResWord(rwrecord, 'RECORD         ', syrecord);
  327.     BuildResWord(rwmodule, 'MODULE         ', symodule);
  328.     BuildResWord(rwimport, 'IMPORT         ', syimport);
  329.     BuildResWord(rwexport, 'EXPORT         ', syexport);
  330.     firstrw[7] := rwpointer;        (* length: 7 *)
  331.     BuildResWord(rwpointer, 'POINTER        ', othersym);
  332.     firstrw[8] := rwprocedure;      (* length: 8 *)
  333.     firstrw[9] := rwprocedure;      (* length: 9 *)
  334.     BuildResWord(rwprocedure, 'PROCEDURE      ', syproc);
  335.     BuildResWord(rwqualified, 'QUALIFIED      ', syqual);
  336.     firstrw[10] := rwdefinition;    (* length: 10 *)
  337.     BuildResWord(rwdefinition, 'DEFINITION     ', sydefinition);
  338.     firstrw[11] := rwimplementation;(* length: 11 *)
  339.     firstrw[12] := rwimplementation;(* length: 12 *)
  340.     firstrw[13] := rwimplementation;(* length: 13 *)
  341.     firstrw[14] := rwimplementation;(* length: 14 *)
  342.     BuildResWord(rwimplementation, 'IMPLEMENTATION ', syimplementation);
  343.     firstrw[15] := rwx;             (* length: 15 FOR table sentinel *)
  344.  
  345. (* constants for lexical scan *)
  346.     FOR i := ordminchar TO ordmaxchar DO
  347.         charclass[CHR(i)] := illegal;
  348.     END;
  349.     FOR ch := 'a' TO 'z' DO
  350.         charclass[ch] := letter;
  351.         charclass[CAP(ch)] := letter;
  352.     END;
  353.     FOR ch := '0' TO '9' DO
  354.         charclass[ch] := digit;
  355.     END;
  356.     charclass[' '] := special;
  357.     charclass['"'] := chapostrophe;
  358.     charclass['#'] := special;
  359.     charclass['&'] := special;
  360.     charclass["'"] := chapostrophe;
  361.     charclass['('] := chleftparen;
  362.     charclass[')'] := chrightparen;
  363.     charclass['*'] := special;
  364.     charclass['+'] := special;
  365.     charclass[','] := special;
  366.     charclass['-'] := special;
  367.     charclass['.'] := chperiod;
  368.     charclass['/'] := special;
  369.     charclass[':'] := chcolon;
  370.     charclass[';'] := chsemicolon;
  371.     charclass['<'] := chlessthan;
  372.     charclass['='] := special;
  373.     charclass['>'] := chgreaterthan;
  374.     charclass['@'] := special;
  375.     charclass['['] := special;
  376.     charclass[']'] := special;
  377.     charclass['^'] := special;
  378.     charclass['{'] := special;
  379.     charclass['|'] := chbar;
  380.     charclass['}'] := special;
  381.     symbolclass[illegal] := othersym;
  382.     symbolclass[special] := othersym;
  383.     symbolclass[chapostrophe] := otherword;
  384.     symbolclass[chleftparen] := leftparen;
  385.     symbolclass[chrightparen] := rightparen;
  386.     symbolclass[chperiod] := period;
  387.     symbolclass[digit] := intconst;
  388.     symbolclass[chcolon] := colon;
  389.     symbolclass[chsemicolon] := semicolon;
  390.     symbolclass[chlessthan] := othersym;
  391.     symbolclass[chgreaterthan] := othersym;
  392.     symbolclass[chbar] := bar;
  393.     symbolclass[letter] := ident;
  394. END StructConsts;
  395.  
  396. (* FlushLine/WriteError/ReadLine convert between files and lines. *)
  397.  
  398. PROCEDURE FlushLine;
  399. (* Write buffer into output file *)
  400. VAR
  401.     i, j, vircol : outrange;        (* loop index *)
  402.     nonblankseen : BOOLEAN;
  403. BEGIN
  404.     WITH outline DO
  405.         WHILE blanklns > 0 DO
  406.             WriteLn;
  407.             blanklns := blanklns - 1;
  408.         END;
  409.         IF len > 0 THEN
  410.             vircol := 0;
  411.             nonblankseen := FALSE;
  412.                     (* set this to TRUE if you don't want blanks to tab conversion *)
  413.             FOR i := 0 TO len - 1 DO
  414.                 IF buf[i+1] <> ' ' THEN
  415.                     IF NOT nonblankseen THEN
  416.                         LOOP
  417.                             j := (vircol DIV tabinterval + 1) * tabinterval;
  418.                             IF j > i THEN
  419.                                 EXIT;
  420.                             END;
  421.                             Write(TAB);
  422.                             vircol := j;
  423.                         END;
  424.                     END;
  425.                     nonblankseen := TRUE;
  426.                     WHILE vircol < i DO
  427.                         Write(' ');
  428.                         vircol := vircol + 1;
  429.                     END;
  430.                     Write(buf[i+1]);
  431.                     vircol := i + 1;
  432.                 END;
  433.             END;
  434.             WriteLn;
  435.             len := 0;
  436.         END;
  437.     END;
  438. END FlushLine;
  439.  
  440. PROCEDURE WriteError(error : errortype);
  441. (* report error to output *)
  442. VAR
  443.     i, ix : inrange;                (* loop index, limit *)
  444. BEGIN
  445.     FlushLine;
  446.     WriteString('(* !!! error, ');
  447.     CASE error OF
  448.     longline:
  449.         WriteString('shorter line');
  450.     | noendcomm:
  451.         WriteString('END OF comment');
  452.     | notquote:
  453.         WriteString("final ' on line");
  454.     | longword:
  455.         WriteString('shorter word');
  456.     | notdo:
  457.         WriteString('"DO"');
  458.     | notof:
  459.         WriteString('"OF"');
  460.     | notend:
  461.         WriteString('"END"');
  462.     | notthen:
  463.         WriteString('"THEN"');
  464.     | notbegin:
  465.         WriteString('"BEGIN"');
  466.     | notuntil:
  467.         WriteString('"UNTIL"');
  468.     | notident:
  469.         WriteString('"identifier"');
  470.     | notsemicolon:
  471.         WriteString('";"');
  472.     | notperiod:
  473.         WriteString('"."');
  474.     | notcolon:
  475.         WriteString('":"');
  476.     | notparen:
  477.         WriteString('")"');
  478.     | noeof:
  479.         WriteString('END OF file');
  480.     END;
  481.     WriteString(' expected');
  482.     IF error >= longword THEN
  483.         WriteString(', NOT "');
  484.         WITH inline DO
  485.             WITH curword DO
  486.                 IF size > maxrwlen THEN
  487.                     ix := maxrwlen
  488.                 ELSE
  489.                     ix := size;
  490.                 END;
  491.                 FOR i := 1 TO ix DO
  492.                     Write(buf[base + i]);
  493.                 END;
  494.             END;
  495.         END;
  496.         Write('"');
  497.     END;
  498.     IF error = noeof THEN
  499.         WriteString(', FORMATTING STOPS');
  500.     END;
  501.     WriteString(' !!! *)');
  502.     WriteLn;
  503. END WriteError;
  504.  
  505. PROCEDURE ReadLine;
  506. (* Read line into input buffer *)
  507. VAR
  508.     c : CHAR;                       (* input character *)
  509.     nonblank : BOOLEAN;             (* is char other than space? *)
  510.     i : INTEGER;
  511. BEGIN
  512.     WITH inline DO
  513.         len := 0;
  514.         LOOP
  515.             Read(c);
  516.             IF Done THEN
  517.                 endoffile := Done;
  518.                 EXIT;
  519.             END;
  520.             IF c = NEWLINE THEN
  521.                 EXIT;
  522.             END;
  523.             IF c < ' ' THEN (* convert ISO control chars (except leading form feed) to spaces *)
  524.                 IF c = TAB THEN
  525.                     (* ISO TAB char *)
  526.                     c := ' ';
  527.                     (* add last space at end *)
  528.                     WHILE len MOD 8 <> 7 DO
  529.                         len := len + 1;
  530.                         IF len < maxinlen THEN
  531.                             buf[len] := c;
  532.                         END;
  533.                     END;
  534.                     (* END tab handling *)
  535.                 ELSIF (c <> FF) OR (len > 0) THEN
  536.                     c := ' ';
  537.                 END;
  538.             END;            (* END ISO control char conversion *)
  539.             len := len + 1;
  540.             IF len < maxinlen THEN
  541.                 buf[len] := c;
  542.             END;
  543.         END;
  544.         IF NOT endoffile THEN
  545.             IF len >= maxinlen THEN
  546.                     (* input line too long *)
  547.                 WriteError(longline);
  548.                 len := maxinlen - 1;
  549.             END;
  550.             WHILE (len > 0) AND (buf[len] = ' ') DO
  551.                 len := len - 1;
  552.             END;
  553.         END;
  554.         len := len + 1;         (* add exactly ONE trailing blank *)
  555.         buf[len] := ' ';
  556.         index := 0;
  557.     END;
  558. END ReadLine;
  559.  
  560. PROCEDURE GetChar;
  561. (* get next char from input buffer *)
  562. BEGIN
  563.     WITH inline DO
  564.         index := index + 1;
  565.         ch := buf[index];
  566.     END;
  567. END GetChar;
  568.  
  569. PROCEDURE NextChar() : CHAR;
  570. (* look at next char in input buffer *)
  571. BEGIN
  572.     RETURN inline.buf[inline.index + 1];
  573. END NextChar;
  574.  
  575. PROCEDURE StartWord(startclass : firstclass);
  576. (* note beginning of word, and count preceding lines and spaces *)
  577. VAR
  578.     first : BOOLEAN;                (* is word the first on input line? *)
  579. BEGIN
  580.     first := FALSE;
  581.     WITH inline DO
  582.         WITH curword DO
  583.             whenfirst := startclass;
  584.             blanklncount := 0;
  585.             WHILE (index >= len) AND NOT endoffile DO
  586.                 IF len = 1 THEN
  587.                     blanklncount := blanklncount + 1;
  588.                 END;
  589.                 IF startclass = contuncomm THEN
  590.                     FlushLine
  591.                 ELSE
  592.                     first := TRUE;
  593.                 END;
  594.                 ReadLine;
  595.                     (* with exactly ONE trailing blank *)
  596.                 GetChar;
  597.                 IF ch = FF THEN
  598.                     FlushLine;
  599.                     Write(FF);
  600.                     blanklncount := 0;
  601.                     GetChar;
  602.                 END;
  603.             END;
  604.             spaces := 0;    (* count leading spaces *)
  605.             IF NOT endoffile THEN
  606.                 WHILE ch = ' ' DO
  607.                     spaces := spaces + 1;
  608.                     GetChar;
  609.                 END;
  610.             END;
  611.             IF first THEN
  612.                 spaces := 1;
  613.             END;
  614.             base := index - 1;
  615.         END;
  616.     END;
  617. END StartWord;
  618.  
  619. PROCEDURE FinishWord;
  620. (* note end of word *)
  621. BEGIN
  622.     WITH inline DO
  623.         WITH curword DO
  624.             puncfollows := (symbol IN datawords) AND (ch <> ' ');
  625.             size := index - base - 1;
  626.         END;
  627.     END;
  628. END FinishWord;
  629.  
  630. PROCEDURE CopyWord(newline : BOOLEAN; pword : wordtype);
  631. (* copy word from input buffer into output buffer *)
  632. VAR
  633.     i : INTEGER;                    (* outline.len excess, loop index *)
  634. BEGIN
  635.     WITH pword DO
  636.         WITH outline DO
  637.             i := maxoutlen - len - spaces - size;
  638.             IF newline OR (i < 0) OR ((i = 0) AND puncfollows) THEN
  639.                 FlushLine;
  640.             END;
  641.             IF len = 0 THEN (* first word on output line *)
  642.                 blanklns := blanklncount;
  643.                 CASE whenfirst OF
  644.                     (* update LOCAL word.spaces *)
  645.                 newclause:
  646.                     spaces := margin;
  647.                 | continue:
  648.                     spaces := margin;
  649.                 | alcomm:
  650.                     spaces := alcommbase;
  651.                 | contalcomm:
  652.                     spaces := alcommbase + commindent;
  653.                 | uncomm:
  654.                     spaces := base;
  655.                 | contuncomm:
  656.                     (* spaces := spaces *);
  657.                 END;
  658.                 IF spaces + size > maxoutlen THEN
  659.                     spaces := maxoutlen - size;
  660.                     (* reduce spaces *)
  661.                     IF spaces < 0 THEN
  662.                         WriteError(longword);
  663.                         size := maxoutlen;
  664.                         spaces := 0;
  665.                     END;
  666.                 END;
  667.             END;
  668.             FOR i := 1 TO spaces DO
  669.                     (* put out spaces *)
  670.                 len := len + 1;
  671.                 buf[len] := ' ';
  672.             END;
  673.             FOR i := 1 TO size DO
  674.                     (* copy actual word *)
  675.                 len := len + 1;
  676.                 buf[len] := inline.buf[base + i];
  677.             END;
  678.         END;
  679.     END;
  680. END CopyWord;
  681.  
  682. PROCEDURE DoComment;                    (* copy aligned or unaligned comment *)
  683.  
  684. PROCEDURE CopyComment(commclass : firstclass; commbase : inrange);
  685. (* copy words of comment *)
  686. VAR
  687.     endcomment : BOOLEAN;           (* end of comment? *)
  688. BEGIN
  689.     WITH curword DO                 (* copy comment begin symbol *)
  690.         whenfirst := commclass;
  691.         spaces := commbase - outline.len;
  692.         CopyWord((spaces < 0) OR (blanklncount > 0), curword);
  693.     END;
  694.     commclass := VAL(firstclass, ORD(commclass)+1);
  695.     WITH inline DO
  696.         REPEAT                  (* loop for successive words *)
  697.             StartWord(commclass);
  698.             endcomment := endoffile;
  699.                     (* premature end? *)
  700.             IF endcomment THEN
  701.                 WriteError(noendcomm)
  702.             ELSE
  703.                 REPEAT
  704.                     IF ch = '*' THEN
  705.                         GetChar;
  706.                         IF ch = ')' THEN
  707.                             endcomment := TRUE;
  708.                             GetChar;
  709.                         END;
  710.                     ELSE
  711.                         GetChar;
  712.                     END;
  713.                 UNTIL (ch = ' ') OR endcomment;
  714.             END;
  715.             FinishWord;
  716.             CopyWord(FALSE, curword)
  717.         UNTIL endcomment;
  718.     END;
  719. END CopyComment;
  720.  
  721. BEGIN                                   (* DoComment *)
  722.     IF curword.base < commthresh THEN
  723.                     (* copy comment without alignment *)
  724.         CopyComment(uncomm, curword.base)
  725.     ELSE                            (* align AND format comment *)
  726.         CopyComment(alcomm, alcommbase);
  727.     END;
  728. END DoComment;
  729.  
  730. PROCEDURE GetSymbol;
  731. (* get next non-comment symbol *)
  732.  
  733. PROCEDURE CopySymbol(symbol : symboltype; pword : wordtype);
  734. (* copy word(s) of symbol *)
  735. BEGIN
  736.     IF symbol = comment THEN
  737.         DoComment;              (* NOTE: DoComment uses global word! *)
  738.         lnpending := TRUE;
  739.     ELSIF symbol = semicolon THEN
  740.         CopyWord(FALSE, pword);
  741.         lnpending := NOT inheader;
  742.     ELSE
  743.         CopyWord(lnpending, pword);
  744.         lnpending := FALSE;
  745.     END;
  746. END CopySymbol;
  747.  
  748. PROCEDURE FindSymbol;
  749. (* find next symbol in input buffer *)
  750.  
  751. VAR
  752.     termch : CHAR;                  (* string terminator *)
  753.     chclass : chartype;             (* classification of leading char *)
  754.  
  755. PROCEDURE CheckResWord;
  756. (* check if current identifier is reserved word/symbol *)
  757. VAR
  758.     rw, rwbeyond : resword;         (* loop index, limit *)
  759.     symword : rwstring;             (* copy of symbol word *)
  760.     i : [-1..maxrwlen];             (* loop index *)
  761. BEGIN
  762.     WITH curword DO
  763.         WITH inline DO
  764.             size := index - base - 1;
  765.             IF size < maxrwlen THEN
  766.                 symword := '               ';
  767.                 FOR i := 1 TO size DO
  768.                     symword[i] := CAP(buf[ base + i]);
  769.                 END;
  770.                 rw := firstrw[size];
  771.                 rwbeyond := firstrw[size + 1];
  772.                 symbol := semicolon;
  773.                 REPEAT
  774.                     IF rw >= rwbeyond THEN
  775.                         symbol := ident
  776.                     ELSIF symword = rwword[rw] THEN
  777.                         symbol := rwsy[rw]
  778.                     ELSE
  779.                         rw := VAL(resword,ORD(rw)+1);
  780.                     END;
  781.                 UNTIL symbol <> semicolon;
  782.             END;
  783.             whenfirst := newclause;
  784.         END;
  785.     END;
  786. END CheckResWord;
  787.  
  788. PROCEDURE GetName;
  789. BEGIN
  790.     WHILE charclass[inline.ch] IN chartypeset{letter, digit} DO
  791.         GetChar;
  792.     END;
  793.     CheckResWord;
  794. END GetName;
  795.  
  796. PROCEDURE GetNumber;
  797. BEGIN
  798.     WITH inline DO
  799.         WHILE charclass[ch] = digit DO
  800.             GetChar;
  801.         END;
  802.         IF ch = '.' THEN
  803.             IF charclass[NextChar()] = digit THEN
  804.                     (* NOTE: NextChar is a function! *)
  805.                 symbol := otherword;
  806.                 GetChar;
  807.                 WHILE charclass[ch] = digit DO
  808.                     GetChar;
  809.                 END;
  810.             END;
  811.         END;
  812.         IF CAP(ch) = 'E' THEN
  813.             symbol := otherword;
  814.             GetChar;
  815.             IF (ch = '+') OR (ch = '-') THEN
  816.                 GetChar;
  817.             END;
  818.             WHILE charclass[ch] = digit DO
  819.                 GetChar;
  820.             END;
  821.         END;
  822.     END;
  823. END GetNumber;
  824.  
  825. PROCEDURE GetStringLiteral;
  826. VAR
  827.     endstring : BOOLEAN;            (* end of string literal? *)
  828. BEGIN
  829.     WITH inline DO
  830.         endstring := FALSE;
  831.         REPEAT
  832.             GetChar;
  833.             IF ch = termch THEN
  834.                 endstring := TRUE;
  835.             ELSIF index >= len THEN
  836.                     (* error, final "'" not on line *)
  837.                 WriteError(notquote);
  838.                 symbol := syeof;
  839.                 endstring := TRUE;
  840.             END;
  841.         UNTIL endstring;
  842.         GetChar;
  843.     END;
  844. END GetStringLiteral;
  845.  
  846. BEGIN                                   (* FindSymbol *)
  847.     StartWord(continue);
  848.     WITH inline DO
  849.         IF endoffile THEN
  850.             symbol := syeof
  851.         ELSE
  852.             termch := ch;   (* save for string literal routine *)
  853.             chclass := charclass[ch];
  854.             symbol := symbolclass[chclass];
  855.             GetChar;        (* second CHAR *)
  856.             CASE chclass OF
  857.             chsemicolon, chrightparen, chleftbrace, special, illegal: ;
  858.             | letter:
  859.                 GetName;
  860.             | digit:
  861.                 GetNumber;
  862.             | chapostrophe:
  863.                 GetStringLiteral;
  864.             | chcolon:
  865.                 IF ch = '=' THEN
  866.                     symbol := othersym;
  867.                     GetChar;
  868.                 END;
  869.             | chlessthan:
  870.                 IF (ch = '=') OR (ch = '>') THEN
  871.                     GetChar;
  872.                 END;
  873.             | chgreaterthan:
  874.                 IF ch = '=' THEN
  875.                     GetChar;
  876.                 END;
  877.             | chleftparen:
  878.                 IF ch = '*' THEN
  879.                     symbol := comment;
  880.                     GetChar;
  881.                 END;
  882.             | chperiod:
  883.                 IF ch = '.' THEN
  884.                     symbol := colon;
  885.                     GetChar;
  886.                 END;
  887.             END;
  888.             FinishWord;
  889.         END;
  890.     END;                            (* FindSymbol *)
  891. END FindSymbol;
  892.  
  893. BEGIN                                   (* GetSymbol *)
  894.     REPEAT
  895.         CopySymbol(symbol, curword);
  896.                     (* copy word for symbol to output *)
  897.         FindSymbol              (* get next symbol *)
  898.     UNTIL symbol <> comment;
  899. END GetSymbol;
  900.  
  901. PROCEDURE StartClause;
  902. (* (this may be a simple clause, or the start of a header) *)
  903. BEGIN
  904.     curword.whenfirst := newclause;
  905.     lnpending := TRUE;
  906. END StartClause;
  907.  
  908. PROCEDURE PassSemicolons;
  909. (* pass consecutive semicolons *)
  910. BEGIN
  911.     WHILE symbol = semicolon DO
  912.         GetSymbol;
  913.         StartClause;
  914.     END;
  915. END PassSemicolons;
  916.  
  917. PROCEDURE StartPart;
  918. (* start program part *)
  919. BEGIN
  920.     WITH curword DO
  921.         IF blanklncount = 0 THEN
  922.             blanklncount := 1;
  923.         END;
  924.     END;
  925. END StartPart;
  926.  
  927. PROCEDURE StartBody;
  928. (* finish header, start body of structure *)
  929. BEGIN
  930.     StartClause;
  931.     margin := margin + indent;
  932. END StartBody;
  933.  
  934. PROCEDURE FinishBody;
  935. (* retract margin *)
  936. BEGIN
  937.     margin := margin - indent;
  938. END FinishBody;
  939.  
  940. PROCEDURE PassPhrase(finalsymbol : symboltype);
  941. (* process symbols until significant symbol encountered *)
  942. VAR
  943.     endsyms : symbolset;            (* complete set of stopping symbols *)
  944. BEGIN
  945.     IF symbol <> syeof THEN
  946.         endsyms := stopsyms;
  947.         INCL(endsyms, finalsymbol);
  948.         REPEAT
  949.             GetSymbol
  950.         UNTIL symbol IN endsyms;
  951.     END;
  952. END PassPhrase;
  953.  
  954. PROCEDURE Expect(expectedsym : symboltype; error : errortype; syms : symbolset);
  955. (* fail if current symbol is not the expected one, then recover *)
  956. BEGIN
  957.     IF symbol = expectedsym THEN
  958.         GetSymbol
  959.     ELSE
  960.         WriteError(error);
  961.         INCL(syms, expectedsym);
  962.         WHILE NOT (symbol IN syms) DO
  963.             GetSymbol;
  964.         END;
  965.         IF symbol = expectedsym THEN
  966.             GetSymbol;
  967.         END;
  968.     END;
  969. END Expect;
  970.  
  971. PROCEDURE Heading;
  972. (* process heading for program or procedure *)
  973.  
  974. PROCEDURE MatchParens;                  (* process parentheses in heading *)
  975. VAR
  976.     endsyms : symbolset;
  977. BEGIN
  978.     GetSymbol;
  979.     WHILE NOT (symbol IN recendsyms) DO
  980.         IF symbol = leftparen THEN
  981.             MatchParens
  982.         ELSE
  983.             GetSymbol;
  984.         END;
  985.     END;
  986.     endsyms := stopsyms + recendsyms;
  987.     Expect(rightparen, notparen, endsyms);
  988. END MatchParens;
  989.  
  990. BEGIN                                   (* heading *)
  991.     GetSymbol;
  992.     PassPhrase(leftparen);
  993.     IF symbol = leftparen THEN
  994.         inheader := TRUE;
  995.         MatchParens;
  996.         inheader := FALSE;
  997.     END;
  998.     IF symbol = colon THEN
  999.         PassPhrase(semicolon);
  1000.     END;
  1001.     Expect(semicolon, notsemicolon, stopsyms);
  1002.  
  1003. END Heading;
  1004.  
  1005. PROCEDURE DoRecord;
  1006. (* process record declaration *)
  1007. BEGIN
  1008.     GetSymbol;
  1009.     StartBody;
  1010.     PassFields(FALSE);
  1011.     FinishBody;
  1012.     Expect(syend, notend, recendsyms);
  1013. END DoRecord;
  1014.  
  1015. PROCEDURE DoVariant;
  1016. (* process (case) variant part *)
  1017. BEGIN
  1018.     PassPhrase(syof);
  1019.     Expect(syof, notof, stopsyms);
  1020.     StartBody;
  1021.     PassFields(TRUE);
  1022.     FinishBody;
  1023. END DoVariant;
  1024.  
  1025. PROCEDURE DoParens(forvariant : BOOLEAN);
  1026. (* process parentheses in record *)
  1027. BEGIN
  1028.     GetSymbol;
  1029.     IF forvariant THEN
  1030.         StartBody;
  1031.     END;
  1032.     PassFields(FALSE);
  1033.     lnpending := FALSE;             (* for empty field list *)
  1034.     Expect(rightparen, notparen, recendsyms);
  1035.     IF forvariant THEN
  1036.         FinishBody;
  1037.     END;
  1038. END DoParens;
  1039.  
  1040. PROCEDURE PassFields(forvariant : BOOLEAN);
  1041. (* process declarations *)
  1042. BEGIN
  1043.     WHILE NOT (symbol IN recendsyms) DO
  1044.         IF symbol = semicolon THEN
  1045.             PassSemicolons
  1046.         ELSIF symbol = syrecord THEN
  1047.             DoRecord
  1048.         ELSIF symbol = sycase THEN
  1049.             DoVariant
  1050.         ELSIF symbol = leftparen THEN
  1051.             DoParens(forvariant)
  1052.         ELSE
  1053.             GetSymbol;
  1054.         END;
  1055.     END;
  1056. END PassFields;
  1057.  
  1058. PROCEDURE Statement;
  1059. (* process statement *)
  1060. BEGIN
  1061.     CASE symbol OF
  1062.     sycase:
  1063.         CaseStatement;
  1064.         Expect(syend, notend, stmtendsyms);
  1065.     | syif:
  1066.         IfStatement;
  1067.         Expect(syend, notend, stmtendsyms);
  1068.     | syloop:
  1069.         LoopStatement;
  1070.         Expect(syend, notend, stmtendsyms);
  1071.     | syrepeat:
  1072.         RepeatStatement;
  1073.     | forwhilewith:
  1074.         ForWhileWithStatement;
  1075.         Expect(syend, notend, stmtendsyms);
  1076.     | ident:
  1077.         AssignmentProccall;
  1078.     | semicolon: ;
  1079.     END;
  1080. END Statement;
  1081.  
  1082. PROCEDURE AssignmentProccall;
  1083. (* pass an assignment statement or procedure call *)
  1084. BEGIN
  1085.     WHILE NOT (symbol IN stmtendsyms) DO
  1086.         GetSymbol;
  1087.     END;
  1088. END AssignmentProccall;
  1089.  
  1090. PROCEDURE StatementSequence;
  1091. (* process sequence of statements *)
  1092. BEGIN
  1093.     Statement;
  1094.     LOOP
  1095.         IF symbol <> semicolon THEN
  1096.             EXIT;
  1097.         END;
  1098.         GetSymbol;
  1099.         Statement;
  1100.     END;
  1101. END StatementSequence;
  1102.  
  1103. PROCEDURE IfStatement;
  1104. (* process if statement *)
  1105. BEGIN
  1106.     PassPhrase(sythen);
  1107.     Expect(sythen, notthen, stopsyms);
  1108.     StartBody;
  1109.     StatementSequence;
  1110.     FinishBody;
  1111.     WHILE symbol = syelsif DO
  1112.         StartClause;
  1113.         PassPhrase(sythen);
  1114.         Expect(sythen, notthen, stopsyms);
  1115.         StartBody;              (* new line after 'THEN' *)
  1116.         StatementSequence;
  1117.         FinishBody;
  1118.     END;
  1119.     IF symbol = syelse THEN
  1120.         StartClause;
  1121.         GetSymbol;
  1122.         StartBody;              (* new line after 'ELSE' *)
  1123.         StatementSequence;
  1124.         FinishBody;
  1125.     END;
  1126. END IfStatement;
  1127.  
  1128. PROCEDURE CaseStatement;
  1129. (* process case statement *)
  1130. BEGIN
  1131.     PassPhrase(syof);
  1132.     Expect(syof, notof, stopsyms);
  1133.     StartClause;
  1134.     OneCase;
  1135.     WHILE symbol = bar DO
  1136.         GetSymbol;
  1137.         OneCase;
  1138.     END;
  1139.     IF symbol = syelse THEN
  1140.         GetSymbol;
  1141.         StartBody;
  1142.         StatementSequence;
  1143.         FinishBody;
  1144.     END;
  1145. END CaseStatement;
  1146.  
  1147. PROCEDURE OneCase;
  1148. (* process one case clause *)
  1149. BEGIN
  1150.     IF NOT (symbol IN symbolset{bar, syelse}) THEN
  1151.         PassPhrase(colon);
  1152.         Expect(colon, notcolon, stopsyms);
  1153.         StartBody;              (* new line, indent after colon *)
  1154.         StatementSequence;
  1155.         FinishBody;             (* left-indent after case *)
  1156.     END;
  1157. END OneCase;
  1158.  
  1159. PROCEDURE RepeatStatement;
  1160. (* process repeat statement *)
  1161. BEGIN
  1162.     GetSymbol;
  1163.     StartBody;                      (* new line, indent after 'REPEAT' *)
  1164.     StatementSequence;
  1165.     FinishBody;                     (* left-ident after UNTIL *)
  1166.     StartClause;                    (* new line before UNTIL *)
  1167.     Expect(syuntil, notuntil, stmtendsyms);
  1168.     PassPhrase(semicolon);
  1169. END RepeatStatement;
  1170.  
  1171. PROCEDURE LoopStatement;
  1172. (* process loop statement *)
  1173. BEGIN
  1174.     GetSymbol;
  1175.     StartBody;                      (* new line, indent after LOOP *)
  1176.     StatementSequence;
  1177.     FinishBody;                     (* left-ident before END *)
  1178. END LoopStatement;
  1179.  
  1180. PROCEDURE ForWhileWithStatement;
  1181. (* process for, while, or with statement *)
  1182. BEGIN
  1183.     PassPhrase(sydo);
  1184.     Expect(sydo, notdo, stopsyms);
  1185.     StartBody;
  1186.     StatementSequence;
  1187.     FinishBody;
  1188. END ForWhileWithStatement;
  1189.  
  1190. PROCEDURE ProcedureDeclaration;
  1191. (* pass a procedure declaration *)
  1192. BEGIN
  1193.     ProcedureHeading;
  1194.     Block;
  1195.     Expect(ident, notident, stmtendsyms);
  1196.     Expect(semicolon, notsemicolon, stmtendsyms);
  1197. END ProcedureDeclaration;
  1198.  
  1199. PROCEDURE ProcedureHeading;
  1200. BEGIN
  1201.     StartClause;
  1202.     Heading;
  1203. END ProcedureHeading;
  1204.  
  1205. PROCEDURE Block;
  1206. BEGIN
  1207.     WHILE symbol IN symbolset{declarator, symodule, syproc} DO
  1208.         Declaration;
  1209.     END;
  1210.     IF symbol = sybegin THEN
  1211.         GetSymbol;
  1212.         StartBody;
  1213.         StatementSequence;
  1214.         FinishBody;
  1215.     END;
  1216.     Expect(syend, notend, stmtendsyms);
  1217. END Block;
  1218.  
  1219. PROCEDURE Declaration;
  1220. BEGIN
  1221.     IF symbol = declarator THEN
  1222.         StartClause;            (* CONST, TYPE, VAR *)
  1223.         GetSymbol;
  1224.         StartBody;
  1225.         REPEAT
  1226.             PassPhrase(syrecord);
  1227.             IF symbol = syrecord THEN
  1228.                 DoRecord;
  1229.             END;
  1230.             IF symbol = semicolon THEN
  1231.                 PassSemicolons;
  1232.             END;
  1233.         UNTIL symbol IN headersyms;
  1234.         FinishBody;
  1235.     ELSIF symbol = symodule THEN
  1236.         ModuleDeclaration;
  1237.     ELSIF symbol = syproc THEN
  1238.         ProcedureDeclaration;
  1239.     END;
  1240. END Declaration;
  1241.  
  1242. PROCEDURE ModuleDeclaration;
  1243. BEGIN
  1244.     PassPhrase(semicolon);
  1245.     PassSemicolons;
  1246.     WHILE symbol IN symbolset{syimport, syexport, syfrom} DO
  1247.         ImportExport;
  1248.     END;
  1249.     Block;
  1250.     Expect(ident, notident, stmtendsyms);
  1251. END ModuleDeclaration;
  1252.  
  1253. PROCEDURE ImportExport;
  1254. BEGIN
  1255.     IF symbol = syfrom THEN
  1256.         PassPhrase(syimport);
  1257.     END;
  1258.     IF symbol = syimport THEN
  1259.         GetSymbol;
  1260.     ELSIF symbol = syexport THEN
  1261.         GetSymbol;
  1262.         IF symbol = syqual THEN
  1263.             GetSymbol;
  1264.         END;
  1265.     END;
  1266.     StartBody;
  1267.     PassPhrase(semicolon);
  1268.     FinishBody;
  1269.     GetSymbol;
  1270. END ImportExport;
  1271.  
  1272. PROCEDURE OneDefinition;
  1273. BEGIN
  1274.     IF symbol = declarator THEN
  1275.         Declaration;
  1276.     ELSIF symbol = syproc THEN
  1277.         ProcedureHeading;
  1278.     END;
  1279. END OneDefinition;
  1280.  
  1281. PROCEDURE DefinitionModule;
  1282. BEGIN
  1283.     GetSymbol;
  1284.     PassPhrase(semicolon);
  1285.     GetSymbol;
  1286.     WHILE symbol IN symbolset{syimport, syexport, syfrom} DO
  1287.         ImportExport;
  1288.     END;
  1289.     WHILE symbol IN symbolset{declarator, syproc} DO
  1290.         OneDefinition;
  1291.     END;
  1292.     Expect(syend, notend, stmtendsyms);
  1293.     GetSymbol;
  1294.     Expect(period, notperiod, stmtendsyms);
  1295. END DefinitionModule;
  1296.  
  1297. PROCEDURE ProgramModule;
  1298. BEGIN
  1299.     ModuleDeclaration;
  1300.     Expect(period, notperiod, stmtendsyms);
  1301. END ProgramModule;
  1302.  
  1303. PROCEDURE CompilationUnit;
  1304. BEGIN
  1305.     IF symbol = syimplementation THEN
  1306.         GetSymbol;
  1307.         ProgramModule;
  1308.     ELSIF symbol = sydefinition THEN
  1309.         DefinitionModule;
  1310.     ELSE
  1311.         ProgramModule;
  1312.     END;
  1313. END CompilationUnit;
  1314.  
  1315. PROCEDURE CopyRemainder;
  1316. (* copy remainder of input *)
  1317. BEGIN
  1318.     WriteError(noeof);
  1319.     WITH inline DO
  1320.         REPEAT
  1321.             CopyWord(FALSE, curword);
  1322.             StartWord(contuncomm);
  1323.             IF NOT endoffile THEN
  1324.                 REPEAT
  1325.                     GetChar
  1326.                 UNTIL ch = ' ';
  1327.             END;
  1328.             FinishWord;
  1329.         UNTIL endoffile;
  1330.     END;
  1331. END CopyRemainder;
  1332.  
  1333. PROCEDURE Initialize;
  1334. (* initialize global variables *)
  1335. BEGIN
  1336.     WITH inline DO
  1337.         endoffile := FALSE;
  1338.         ch := ' ';
  1339.         index := 0;
  1340.         len := 0;
  1341.     END;
  1342.     WITH outline DO
  1343.         blanklns := 0;
  1344.         len := 0;
  1345.     END;
  1346.     WITH curword DO
  1347.         whenfirst := contuncomm;
  1348.         puncfollows := FALSE;
  1349.         blanklncount := 0;
  1350.         spaces := 0;
  1351.         base := 0;
  1352.         size := 0;
  1353.     END;
  1354.     margin := initmargin;
  1355.     lnpending := FALSE;
  1356.     symbol := othersym;
  1357. END Initialize;
  1358.  
  1359. BEGIN
  1360.     StructConsts;
  1361.     Initialize;
  1362. (* Files may be opened here. *)
  1363.     GetSymbol;
  1364.     CompilationUnit;
  1365.     IF NOT inline.endoffile THEN
  1366.         CopyRemainder;
  1367.     END;
  1368.     FlushLine;
  1369. END Modula2PrettyPrinter.
  1370. SHAR_EOF
  1371. if test 33277 -ne "`wc -c 'm2p.mod'`"
  1372. then
  1373.     echo shar: error transmitting "'m2p.mod'" '(should have been 33277 characters)'
  1374. fi
  1375. if test -f 'InOut.def'
  1376. then
  1377.     echo shar: over-writing existing file "'InOut.def'"
  1378. fi
  1379. cat << \SHAR_EOF > 'InOut.def'
  1380. DEFINITION MODULE InOut;
  1381. EXPORT
  1382.     Done, Read, Write, WriteLn, WriteString;
  1383.  
  1384. VAR
  1385.     Done        : BOOLEAN;
  1386.  
  1387. PROCEDURE Read(VAR ch : CHAR);
  1388.  
  1389. PROCEDURE Write(ch : CHAR);
  1390.  
  1391. PROCEDURE WriteLn;
  1392.  
  1393. PROCEDURE WriteString(s : ARRAY OF CHAR);
  1394.  
  1395. END InOut.
  1396. SHAR_EOF
  1397. if test 233 -ne "`wc -c 'InOut.def'`"
  1398. then
  1399.     echo shar: error transmitting "'InOut.def'" '(should have been 233 characters)'
  1400. fi
  1401. if test -f 'InOut.c'
  1402. then
  1403.     echo shar: over-writing existing file "'InOut.c'"
  1404. fi
  1405. cat << \SHAR_EOF > 'InOut.c'
  1406. #include    <stdio.h>
  1407.  
  1408. int InOut_Done    = 0;
  1409.  
  1410. InOut__init()
  1411. {
  1412.     InOut_Done = 0;
  1413. }
  1414.  
  1415. InOut_Read(c)
  1416.     char    *c;
  1417. {
  1418.     register char    ch;
  1419.  
  1420.     if ((ch = getchar()) == EOF)
  1421.         InOut_Done = 1;
  1422.     else
  1423.         *c = ch & 0177;
  1424. }
  1425.  
  1426. InOut_Write(c)
  1427.     char    c;
  1428. {
  1429.     putchar(c);
  1430. }
  1431.  
  1432. InOut_WriteLn()
  1433. {
  1434.     putchar('\n');
  1435. }
  1436.  
  1437. InOut_WriteString(s, l)
  1438.     char    *s;
  1439.     int    l;
  1440. {
  1441.     while (l-- > 0)
  1442.         putchar(*s++);
  1443. }
  1444. SHAR_EOF
  1445. if test 357 -ne "`wc -c 'InOut.c'`"
  1446. then
  1447.     echo shar: error transmitting "'InOut.c'" '(should have been 357 characters)'
  1448. fi
  1449. #    End of shell archive
  1450. exit 0
  1451. -- 
  1452. UUCP: ..!{allegra,decvax,seismo}!rochester!ken ARPA: ken@rochester.arpa
  1453. USnail:    Dept. of Comp. Sci., U. of Rochester, NY 14627. Voice: Ken!
  1454.  
  1455.